/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package  com.inspur.edp.cef.core.changesetmgr;

import com.inspur.edp.cef.api.changeset.IChangesetManager;
import com.inspur.edp.cef.api.exceptions.ErrorCodes;
import com.inspur.edp.cef.api.session.ICefSession;
import com.inspur.edp.cef.api.session.ICefSessionItem;
import com.inspur.edp.cef.entity.changeset.ChangeDetailMerger;
import com.inspur.edp.cef.entity.changeset.IChangeDetail;
import com.inspur.edp.cef.entity.changeset.ModifyChangeDetail;
import com.inspur.edp.cef.entity.dependenceTemp.DataValidator;
import io.iec.edp.caf.common.JSONSerializer;
import io.iec.edp.caf.commons.exception.CAFRuntimeException;
import io.iec.edp.caf.commons.exception.ExceptionLevel;
import java.util.Map;

public abstract class ChangesetManager implements IChangesetManager
{
	private ICefSession privateSession;
	protected final ICefSession getSession()
	{
		return privateSession;
	}

	private String privateEntityType;
	protected final String getEntityType()
	{
		return privateEntityType;
	}

	protected ChangesetManager(String entityType, ICefSession session)
	{
		DataValidator.checkForNullReference(session, "session");
		DataValidator.checkForEmptyString(entityType, "entityType");

		privateSession = session;
		privateEntityType = entityType;
	}

	//公开给业务开发的GetChange要进行复制, 否则业务开发修改变更集会直接影响到这里
	public final IChangeDetail getChange(String id, int level)
	{
		DataValidator.checkForEmptyString(id, "id");

		Map<Integer, IChangeDetail> levelChanges = getLevelChanges(id, false);
		return levelChanges == null ? null : levelChanges.get(level);
	}

	public final IChangeDetail getChange(String id)
	{
		DataValidator.checkForEmptyString(id, "id");

		java.util.Map<Integer, IChangeDetail> levels = getLevelChanges(id, false);
		if(levels == null || levels.isEmpty()) {
			return null;
		}
		return getMaxLevelBuffer(levels);
	}

	private java.util.Map<String, java.util.Map<Integer, IChangeDetail>> entityChanges;
	private java.util.Map<String, java.util.Map<Integer, IChangeDetail>> getChanges()
	{
		if (entityChanges != null) {
			return entityChanges;
		}
		ICefSessionItem sessionItem = getSession().getSessionItems().get(getEntityType());
		if(sessionItem == null) {
			throw new RuntimeException("sessionItem not exists");
		}
		return sessionItem.getChanges();
	}

	private java.util.Map<Integer, IChangeDetail> getLevelChanges(String id, boolean addIfNotExist)
	{
		java.util.Map<String, java.util.Map<Integer, IChangeDetail>> changes = getChanges();
		java.util.Map<Integer, IChangeDetail> result = addIfNotExist
				? changes.computeIfAbsent(id, key -> new java.util.LinkedHashMap<>())
				: changes.get(id);
		return result;
	}

	public final void acceptChange(String id, int level)
	{
		DataValidator.checkForEmptyString(id, "id");
		if (level == 0) {
			throw new RuntimeException("level"+level+"");
		}

		//只能从最大层逐级accept, 检查大于level层的change必须为null;
		java.util.Map<Integer, IChangeDetail> levelChanges = getLevelChanges(id, true);
		for(Map.Entry<Integer, IChangeDetail> pair : levelChanges.entrySet()){
			if(pair.getKey() > level && pair.getValue() != null){
				throwCrossLevelChgMerging(pair.getValue());
			}
		}

		IChangeDetail levelChange = levelChanges.get(level);
		if(levelChange ==null) {
			return;
		}

		IChangeDetail lowerLevelChange = levelChanges.get(level - 1);
		levelChanges.put(level - 1, lowerLevelChange == null ? levelChange
				: ChangeDetailMerger.mergeChangeDetail(levelChange, lowerLevelChange));
		levelChanges.put(level, null);
	}

	private static void throwCrossLevelChgMerging(IChangeDetail changeDetail) {
		StringBuilder sb = new StringBuilder();
		sb.append("请不要在BE自定义动作或vo动作中直接修改数据, 仅支持在BE实体动作中修改当前实体数据.").append(System.lineSeparator())
				.append("change:").append(JSONSerializer.serialize(changeDetail));

		throw new CAFRuntimeException("pfcommon", ErrorCodes.Changeset_CrossLevelMerging, sb.toString(),
				null, ExceptionLevel.Error, false);
	}

	public final void rejectChange(String id, int level)
	{
		DataValidator.checkForEmptyString(id, "id");

		//只能从最大层逐级Reject, 检查大于level层的change必须为null;
		java.util.Map<Integer, IChangeDetail> levelChanges = getLevelChanges(id, true);
		for (int i = 1; i < levelChanges.size(); i++)
		{
			if(levelChanges.containsKey(i+level)==false)
				break;
			IChangeDetail levelChange=levelChanges.get(i+level);
			if (levelChange != null) {
				throwCrossLevelChgMerging(levelChange);
			}
		}

		levelChanges.put(level, null);
	}

	protected final void setChange(String id, int level, IChangeDetail change)
	{
		DataValidator.checkForEmptyString(id, "id");

		getLevelChanges(id, true).put(level, change);
	}

	protected final void setChange(String id, IChangeDetail change)
	{
		DataValidator.checkForEmptyString(id, "id");

		java.util.Map<Integer, IChangeDetail> levels = getLevelChanges(id, true);

		levels.put(getMaxLevel(levels),change);
	}

	//将新产生的变更合并到特定外层变更上
	protected final void appendChange(String id, int level, IChangeDetail change)
	{
		DataValidator.checkForEmptyString(id, "id");

		Map<Integer, IChangeDetail> levelChange = getLevelChanges(id, true);
		IChangeDetail existingChange = levelChange.get(level);
		levelChange.put(level, existingChange == null ? change.clone()
				: ChangeDetailMerger.mergeChangeDetail(change, existingChange));
	}

	public final void initChange(String id, int level, IChangeDetail change)
	{
		java.util.Map<Integer, IChangeDetail> levelChanges = getLevelChanges(id, true);
		if (levelChanges.isEmpty()==false)
		{
			throw new UnsupportedOperationException();
		}

		levelChanges.put(level, change);
	}
	private IChangeDetail getMaxLevelBuffer(java.util.Map<Integer, IChangeDetail> buffer)
	{
		return buffer.get(getMaxLevel(buffer));
	}

	private Integer getMaxLevel(java.util.Map<Integer, IChangeDetail> buffer) {
		int value = 0;
		for (Integer level : buffer.keySet()) {
			if (level > value) {
				value = level;
			}
		}
		return value;
	}

	public void remove(String id){
		getChanges().remove(id);
	}

	public void clear(){
		getChanges().clear();
	}
}
